#include "zbI2C.h"

u8 i2cfail = false;
unsigned char I2CTimeout = 0;

void initI2CMain(void)
{
    I2C_InitTypeDef  I2C_InitStructure;

	// enable clocks for AFio and gpiob, these are in the "high speed" clock PB2
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | I2C_SDA_GPIO_CLK, ENABLE);
	// enable clock for i2c
    RCC_APB1PeriphClockCmd (I2C_CLK, ENABLE);

	initI2CMainGpio();

	// enable the i2c
	I2C_Cmd(I2C_MAIN, ENABLE);

    // Release I2CG from reset state
    //RCC_APB1PeriphResetCmd(I2C_CLK, DISABLE);

	// i2c configure
    I2C_DeInit(I2C_MAIN);
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 =  0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000;


    I2C_Init(I2C_MAIN, &I2C_InitStructure);
    
    I2C_AcknowledgeConfig(I2C_MAIN, ENABLE);
}

void disableI2CMain(void)
{
	RCC_APB1PeriphResetCmd(I2C_CLK, DISABLE);
}

void updateI2CMain(void)
{
	u8 devAddr = 0;
	u8 intAddr = 0;
	u16 data = 0;
	u8 cont = false;
	// if there has been a change in the enabled state
	// of at least some perph
	if(mygVars->enabled != newgVars->enabled)
	{
		// if we were off 
		if(!(mygVars->enabled & enI2C))
		{
			// and now we are on
			if(newgVars->enabled & enI2C)
			{
				initI2CMain();	// actually i2c2 !
				// update to know that we are on now
				mygVars->enabled = (mygVars->enabled | enI2C);
				cont = true;
			}
		}
		// if we were on
		if(mygVars->enabled & enI2C)
		{
			// and now we are off
			if(!(newgVars->enabled & enI2C))
			{
				disableI2CMain();
				// clear bit so we are off next time
				mygVars->enabled = (mygVars->enabled & !enI2C);
			}
		}
	}

	// if they send us this they never acked the response we sent them ! 
	if(newgVars->i2cTXBuff == -1)
	{
		return;
	}

	if(!(mygVars->enabled & enI2C) && (cont == false))
	{
		return;
	}


	// check to see if there is new data for us, else return
	// we dont save the new value we just use it once then forget 
	if(newgVars->i2cTXBuff == 0)
	{
		return;
	}
	/*
	- 1 byte Address of device (if lowest bit cleared its write, else read) 
	- 1 byte Internal register Address 
	- 2 Size or Data, in a read this is the size to read in a write this is the data
	*/
	// setup all of the vars  
	devAddr 	= (u8)((newgVars->i2cTXBuff >> 24) & 0xFF);
	intAddr 	= (u8)((newgVars->i2cTXBuff >> 16) & 0xFF);
	data 		= (u16)((newgVars->i2cTXBuff));

	// bad malformed command, inavlid i2c address
	if(devAddr == 0)
	{
		mygVars->status |= ERR_BADCMD;
		return;
	}

	// if we were in err b4 try to reinit/reset
	if(mygVars->status &= ERR_I2CTO)
	{
		mygVars->status |= ~ERR_I2CTO;
		disableI2CMain();
		initI2CMain();
	}

	// if its a read command, just read the first 2 bytes
	if(devAddr & 0x01)// if lsb = 1 its a read
	{
		mygVars->i2cRXBuff = ReadReg(devAddr, intAddr);
	}else{	// if lsb = 0 its a write
		// else do a write with the data
		WriteReg(devAddr, intAddr, data);
	}

	// clear this out
	mygVars->i2cTXBuff = -1;



}

void initI2CMainGpio(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	// remap b8 and b9 to sda scl
	// we have to do this on the board we are using
	// by default pins b8 and b9 are not i2c1 on HY-STM32 board
	if(P8P9_ALTFXN)
	{
		GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
	}

    // GPIOB Configure
    // I2CG SCL and SDA configuration
	// set to alternate function open drain
    GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
}

u16 WriteReg(uint8_t devAddr, u8 intAddr, uint16_t value)
{
   uint32_t Timeout;
   uint8_t bTwo = value;
   uint8_t bOne = value >> 8;

   /* Test on BUSY Flag */
  Timeout = LONG_TIMEOUT;
  while (I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_BUSY)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('a');
  }

    /* Enable the I2C peripheral */
  I2C_GenerateSTART(I2C_MAIN, ENABLE);
  
  /* Test on SB Flag */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_SB)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('b');
  }

  /* Send device address for write */
  I2C_Send7bitAddress(I2C_MAIN, devAddr, I2C_Direction_Transmitter);
  
  /* Test on ADDR Flag */
  Timeout = FLAG_TIMEOUT;
  //while (!I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  while(!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('c');
  }

  /* Send the device's internal address to write to */
  I2C_SendData(I2C_MAIN, intAddr);  
  
  /* Test on TXE FLag (data sent) */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('d');
  }

  /* Send the first byte of data  */
  I2C_SendData(I2C_MAIN, bOne);  
  
  /* Test on TXE FLag (data sent) */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('e');
  }

  /* Send the second byte of data  */
  I2C_SendData(I2C_MAIN, bTwo);  
  
  /* Test on TXE FLag (data sent) */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('f');
  }

  /* Send STOP Condition */
  I2C_GenerateSTOP(I2C_MAIN, ENABLE);

  return 0;

}

uint16_t ReadReg(u8 devAddr, u8 intAddr)
{ 

  uint8_t BufferRX[2] ={0,0};
  uint16_t tmp = 0; 
  uint32_t Timeout;     

   /* Test on BUSY Flag */
  Timeout = LONG_TIMEOUT;
  while (I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_BUSY)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('a');
  }

  /* Send STOP Condition */
  //I2C_GenerateSTOP(I2C, ENABLE);
  
  /* Configure DMA Peripheral */
  DMA_Config(DMA_RX, (uint8_t*)BufferRX, 2);  
  
  /* Enable DMA NACK automatic generation */
  I2C_DMALastTransferCmd(I2C_MAIN, ENABLE);
  
  /* Enable the I2C peripheral */
  I2C_GenerateSTART(I2C_MAIN, ENABLE);
  
  /* Test on SB Flag */
  Timeout = FLAG_TIMEOUT;	 // this was different before might be more right like this
  while(!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_MODE_SELECT)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('b');
  }
  
  /* Send device address for write */
  I2C_Send7bitAddress(I2C_MAIN, devAddr, I2C_Direction_Transmitter);
  
  /* Test on ADDR Flag */
  Timeout = FLAG_TIMEOUT;
  // this is where the device responds or does not
  while(!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('c');
  }
  
  /* Send the device's internal address to write to */
  I2C_SendData(I2C_MAIN, intAddr);  
  
  /* Test on TXE FLag (data sent) */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('d');
  }
  
  /* Send START condition a second time */  
  I2C_GenerateSTART(I2C_MAIN, ENABLE);
  
  /* Test on SB Flag */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_SB)) 
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('e');
  }
  
  /* Send device address for read */
  I2C_Send7bitAddress(I2C_MAIN, devAddr, I2C_Direction_Receiver);
  
  /* Test on ADDR Flag */
  Timeout = FLAG_TIMEOUT;
  while (!I2C_CheckEvent(I2C_MAIN, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))   
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('f');
  }
  
  /* Test on RXE FLag (data sent), wait for first byte */
  Timeout = FLAG_TIMEOUT;
  while ((!I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_RXNE)) && (!I2C_GetFlagStatus(I2C_MAIN,I2C_FLAG_BTF)))  
  {
    if((Timeout--) == 0) return TIMEOUT_UserCallback('g');
  }
  
  // /* Enable I2C DMA request */
  I2C_DMACmd(I2C_MAIN,ENABLE);
  
  // /* Enable DMA RX Channel */
  DMA_Cmd(DMA_I2C_RX_CHANNEL, ENABLE);
  
  // /* Wait until DMA Transfer Complete */
  Timeout = LONG_TIMEOUT;
  while (!DMA_GetFlagStatus(DMA_I2C_RX_TCFLAG))
  {
  if((Timeout--) == 0) return TIMEOUT_UserCallback('h');
  }        
  
  /* Send STOP Condition */
  I2C_GenerateSTOP(I2C_MAIN, ENABLE);
  
  /* Disable DMA RX Channel */
  DMA_Cmd(DMA_I2C_RX_CHANNEL, DISABLE);
  
  /* Disable I2C DMA request */  
  I2C_DMACmd(I2C_MAIN,DISABLE);
  
  /* Clear DMA RX Transfer Complete Flag */
  DMA_ClearFlag(DMA_I2C_RX_TCFLAG);
  
  /*!< Store I2C received data */
  tmp = (uint16_t)(BufferRX[0] << 8);
  tmp |= BufferRX[1];
  
  // we did not timeout
  I2CTimeout = 0;

  /* return a Reg value */
  return (uint16_t)tmp;  
}

static void DMA_Config(DMADirection_TypeDef Direction, uint8_t* buffer, uint8_t NumData)
{
  DMA_InitTypeDef DMA_InitStructure;
  
  /* Initialize the DMA_PeripheralBaseAddr member */
  DMA_InitStructure.DMA_PeripheralBaseAddr = I2C_DR;
  /* Initialize the DMA_MemoryBaseAddr member */
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
   /* Initialize the DMA_PeripheralInc member */
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  /* Initialize the DMA_MemoryInc member */
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  /* Initialize the DMA_PeripheralDataSize member */
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  /* Initialize the DMA_MemoryDataSize member */
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  /* Initialize the DMA_Mode member */
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  /* Initialize the DMA_Priority member */
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
  /* Initialize the DMA_M2M member */
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  
  /* If using DMA for Reception */
  if (Direction == DMA_RX)
  {
    /* Initialize the DMA_DIR member */
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    
    /* Initialize the DMA_BufferSize member */
    DMA_InitStructure.DMA_BufferSize = NumData;
    
    DMA_DeInit(DMA_I2C_RX_CHANNEL);
    
    DMA_Init(DMA_I2C_RX_CHANNEL, &DMA_InitStructure);
  }
   /* If using DMA for Transmission */
  else if (Direction == DMA_TX)
  { 
    /* Initialize the DMA_DIR member */
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    
    /* Initialize the DMA_BufferSize member */
    DMA_InitStructure.DMA_BufferSize = NumData;
    
    DMA_DeInit(DMA_I2C_TX_CHANNEL);
    
    DMA_Init(DMA_I2C_TX_CHANNEL, &DMA_InitStructure);
  }
}

uint16_t TIMEOUT_UserCallback(unsigned char state)
{
	mygVars->status |= ERR_I2CTO;
   	return 0xFFFF;
}

